package avicit.bdp.dgs.standard.service;

import avicit.bdp.common.base.BaseService;
import avicit.bdp.common.service.dto.CommonTypeDTO;
import avicit.bdp.common.service.service.CommonTypeService;
import avicit.bdp.common.utils.BdpLogUtil;
import avicit.bdp.common.utils.CheckParaUtils;
import avicit.bdp.common.utils.ConfigUtils;
import avicit.bdp.common.utils.enums.CommonTypeEnums;
import avicit.bdp.core.constant.Constants;
import avicit.bdp.core.enums.DataStateEnum;
import avicit.bdp.core.poi.util.csv.ExcelUtils;
import avicit.bdp.core.poi.util.csv.style.ExcelExportTitleStyle;
import avicit.bdp.common.service.service.InsertSysExcelImportLogService;
import avicit.bdp.dgs.standard.dao.LookupTableDao;
import avicit.bdp.dgs.standard.dto.DataElementDto;
import avicit.bdp.dgs.standard.dto.LookupTableDto;
import avicit.bdp.dgs.standard.dto.LookupTableValueDto;
import avicit.bdp.dgs.standard.vo.LookupTableImportResult;
import avicit.platform6.api.system.ConvertColumnClient;
import avicit.platform6.api.system.impl.SystemConstant;
import avicit.platform6.commons.utils.BusinessUtil;
import avicit.platform6.commons.utils.ComUtil;
import avicit.platform6.core.context.ThreadContextHelper;
import avicit.platform6.core.exception.BusinessException;
import avicit.platform6.core.rest.msg.QueryRespBean;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import com.github.pagehelper.Page;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author mayi 值域标准管理服务层
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class LookupTableService extends BaseService<LookupTableDao, LookupTableDto> {

  private static final Logger logger = LoggerFactory.getLogger(LookupTableService.class);

  @Resource private LookupTableDao lookupTableDao;
  @Resource private CommonTypeService commonTypeService;
  @Resource private ConvertColumnClient convertColumnClient;
  @Resource private LookupTableValueService lookupTableValueService;
  @Resource private DataElementService dataElementService;
  @Resource private InsertSysExcelImportLogService insertSysExcelImportLog;

  public QueryRespBean<LookupTableDto> getPageList(
      String typeId, String keyWords, Integer status, Integer pageNo, Integer pageSize) throws Exception {
    List<String> typeIdList = new ArrayList<>();
    if (StringUtils.isNotEmpty(typeId)) {
      commonTypeService.typeIdConvert(typeId, typeIdList, CommonTypeEnums.DATA_LOOKUP_TABLE_TYPE);
    }

    QueryRespBean<LookupTableDto> queryRespBean = new QueryRespBean<>();
    List<String> wordSecretList = ThreadContextHelper.getWordSecretList();
    com.github.pagehelper.PageHelper.startPage(pageNo, pageSize);
    Page<LookupTableDto> page = lookupTableDao.getPageList(typeIdList, null, keyWords, status, wordSecretList);
    try {
      valueConvert(page);
    } catch (Exception e) {
      e.printStackTrace();
    }
    queryRespBean.setResult(page);
    BdpLogUtil.log4Query(queryRespBean);
    return queryRespBean;
  }

  private void valueConvert(Page<LookupTableDto> page) {
    // 循环组装请求数据
    Map<String, Set<String>> convertFormData = new HashMap<>(16);
    for (LookupTableDto lookupTableDto : page) {
      BusinessUtil.createConvertSet(
          convertFormData, SystemConstant.USER, lookupTableDto.getCreatedBy());
    }
    if (convertFormData.size() > 0) {
      // 获取请求结果
      Map<String, Map<String, String>> convertResultData =
          convertColumnClient.replace(convertFormData);
      // 循环设置Alias或Name的值
      for (LookupTableDto lookupTableDto : page) {
        lookupTableDto.setStatusString(DataStateEnum.getDescByCode(lookupTableDto.getStatus()));
        lookupTableDto.setCreatedBy(
            BusinessUtil.convertFormat(
                convertResultData, SystemConstant.USER, lookupTableDto.getCreatedBy()));
      }
    }
  }

  public String save(LookupTableDto lookupTableDto) {

    CheckParaUtils.checkStringValidity(lookupTableDto.getTypeId(), logger, "分类必填");

    checkNameUnique(lookupTableDto);

    checkCodeUnique(lookupTableDto);

    lookupTableDto.setId(ComUtil.getId());
    lookupTableDto.setStatus(DataStateEnum.UNDER_EDITING.getCode());

    if (CollectionUtils.isNotEmpty(lookupTableDto.getValueDtoList())) {
      for (LookupTableValueDto valueDto : lookupTableDto.getValueDtoList()) {
        valueDto.setLookupTableId(lookupTableDto.getId());
      }
      lookupTableValueService.batchSave(lookupTableDto.getValueDtoList());
    }

    this.insert(lookupTableDto);
    log4Insert(lookupTableDto);
    return lookupTableDto.getId();
  }

  public void update(LookupTableDto lookupTableDto) {

    CheckParaUtils.checkObjectValidity(lookupTableDto.getCode(), logger, "编码必填");
    CheckParaUtils.checkStringValidity(lookupTableDto.getName(), logger, "名称必填");
    CheckParaUtils.checkStringValidity(lookupTableDto.getTypeId(), logger, "分类必填");

    LookupTableDto oldLookupTable = this.selectByPrimaryKey(lookupTableDto.getId());
    CheckParaUtils.checkDbObjectValidity(
        oldLookupTable, logger, String.format("没有查到数据，请确保id:%s的数据是否存在", lookupTableDto.getId()));

    // 校验标准名称唯一性
    if (!lookupTableDto.getName().equals(oldLookupTable.getName())) {
      checkNameUnique(lookupTableDto);
    }

    // 校验标准编码唯一性
    if (!lookupTableDto.getCode().equals(oldLookupTable.getCode())) {
      checkCodeUnique(lookupTableDto);
    }

    lookupTableDto.setCreationDate(oldLookupTable.getCreationDate());
    this.updateByPrimaryKey(lookupTableDto);
    BdpLogUtil.log4Update(lookupTableDto, oldLookupTable);
  }

  public Boolean batchDelete(String ids) {
    CheckParaUtils.checkStringValidity(ids, logger, "没有传入待删除的ID");
    String[] idArr = ids.split(Constants.COMMA);
    for (String id : idArr) {
      LookupTableDto lookupTableDto = this.selectByPrimaryKey(id);
      CheckParaUtils.checkDbObjectValidity(
          lookupTableDto, logger, String.format("没有查到数据，请确保id:%s的数据是否存在", id));

      // todo 已经被引用的值域标准，不允许直接删除，给出提示:该值域标准已经被引用，不能删除
      // 统计值域标准在数据元中的引用
      DataElementDto dataElementDto = new DataElementDto();
      dataElementDto.setLookupTableId(id);
      List<DataElementDto> dataElementList = dataElementService.selectList(dataElementDto);
      if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(dataElementList)) {
        List<String> dataElementNames =
            dataElementList.stream().map(DataElementDto::getName).collect(Collectors.toList());
        throw new BusinessException(
            "该值域标准已经被[" + String.join(",", dataElementNames) + "]等数据项标准引用，不能删除!");
      }

      this.mapper.deleteByPrimaryKey(id);
      lookupTableValueService.deleteByLookupTableId(id);
      // 记录日志
      BdpLogUtil.log4Delete(lookupTableDto);
    }
    return true;
  }

  public LookupTableDto queryById(String id) {
    LookupTableDto lookupTableDto = this.selectByPrimaryKey(id);
    CheckParaUtils.checkDbObjectValidity(
        lookupTableDto, logger, String.format("没有查到数据，请确保id:%s的数据是否存在", id));

    lookupTableDto.setStatusString(DataStateEnum.getDescByCode(lookupTableDto.getStatus()));
    return lookupTableDto;
  }

  /**
   * 校验名称唯一性
   *
   * @param lookupTableDto 检测对象
   */
  private void checkNameUnique(LookupTableDto lookupTableDto) {
    if (lookupTableDto != null) {
      if (StringUtils.isEmpty(lookupTableDto.getName())) {
        throw new BusinessException("名称不能为空");
      }

      LookupTableDto paramName = new LookupTableDto();
      paramName.setName(lookupTableDto.getName());
      if (this.selectCount(paramName) > 0) {
        throw new BusinessException("名称[" + lookupTableDto.getName() + "]已存在");
      }
    }
  }

  /**
   * 校验代码唯一性
   *
   * @param lookupTableDto 检测对象
   */
  private void checkCodeUnique(LookupTableDto lookupTableDto) {
    if (lookupTableDto != null) {
      if (StringUtils.isEmpty(lookupTableDto.getCode())) {
        throw new BusinessException("代码不能为空");
      }

      LookupTableDto paramCode = new LookupTableDto();
      paramCode.setCode(lookupTableDto.getCode());
      if (this.selectCount(paramCode) > 0) {
        throw new BusinessException("编码[" + lookupTableDto.getCode() + "]已存在");
      }
    }
  }

  public String getIdByCode(String lookupTableCode) {
    return lookupTableDao.getIdByCode(lookupTableCode);
  }

  /**
   * 查询数据元
   *
   * @param projectId projectId
   * @param keyWords 关键词
   * @return 树状数据
   */
  public List<CommonTypeDTO> getTreeList(String projectId, String keyWords) {
    try {
      // 顶级节点
      List<CommonTypeDTO> result = new ArrayList<>();
      // 子节点数据
      Map<String, List<CommonTypeDTO>> map = new HashMap<>();
      List<CommonTypeDTO> commonTypeList;

      CommonTypeDTO commonTypeDTO = new CommonTypeDTO();
      commonTypeDTO.setType(CommonTypeEnums.DATA_LOOKUP_TABLE_TYPE);
      if (StringUtils.isNotEmpty(projectId)) {
        commonTypeDTO.setProjectId(projectId);
      }

      List<CommonTypeDTO> childrenType = commonTypeService.selectList(commonTypeDTO);
      commonTypeList =
          Stream.of(childrenType)
              .flatMap(Collection::stream)
              .distinct()
              .sorted(
                  Comparator.comparing(
                      CommonTypeDTO::getOrderBy, Comparator.nullsLast(Integer::compareTo)))
              .collect(Collectors.toList());

      String rootTypeId = "";
      if (CollectionUtils.isNotEmpty(commonTypeList)) {
        for (CommonTypeDTO commonType : commonTypeList) {
          if ("-1".equals(commonType.getParentId())) {
            result.add(commonType);
            rootTypeId = commonType.getId();
          } else {
            if (map.containsKey(commonType.getParentId())) {
              map.get(commonType.getParentId()).add(commonType);
            } else {
              List<CommonTypeDTO> commonTableDTOList = new ArrayList<>();
              commonTableDTOList.add(commonType);
              map.put(commonType.getParentId(), commonTableDTOList);
            }
          }
        }
      }

      List<String> typeIds = commonTypeService.getChildrenIds(
              CommonTypeEnums.DATA_LOOKUP_TABLE_TYPE, rootTypeId, projectId);
      if (typeIds == null) {
        typeIds = Lists.newArrayList();
      }
      typeIds.add(rootTypeId);
      List<String> wordSecretList = ThreadContextHelper.getWordSecretList();
      List<LookupTableDto> list = this.mapper.getPageList(typeIds, null, keyWords, null, wordSecretList);
      buildChildren(result, map, list);

      // 顶级节点不必返回
      if (result.size() == 1) {
        result = result.get(0).getChildren();
      }
      return result;
    } catch (Exception e) {
      logger.error("查询异常{}", e.getMessage());
      throw new BusinessException(e.getMessage(), e);
    }
  }

  private void buildChildren(
      List<CommonTypeDTO> list,
      Map<String, List<CommonTypeDTO>> map,
      List<LookupTableDto> dtoList) {

    for (CommonTypeDTO commonTypeDTO : list) {
      List<CommonTypeDTO> commonTypeList = map.get(commonTypeDTO.getId());
      if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(commonTypeList)) {
        buildChildren(commonTypeList, map, dtoList);
        commonTypeDTO.setChildren(commonTypeList);
      } else {
        List<CommonTypeDTO> commonTypeDtoList = new ArrayList<>();
        // 叶子节点增加表数据到树
        if (CollectionUtils.isNotEmpty(dtoList)) {
          for (LookupTableDto lookupTableDto : dtoList) {
            if (commonTypeDTO.getId().equals(lookupTableDto.getTypeId())) {
              CommonTypeDTO commonType = new CommonTypeDTO();
              commonType.setId(lookupTableDto.getId());
              commonType.setName(lookupTableDto.getName());
              commonType.setRemark(lookupTableDto.getName());
              commonType.setSelectable(true);
              commonTypeDtoList.add(commonType);
            }
          }
        }

        commonTypeDTO.setChildren(commonTypeDtoList);
      }
    }
  }

  public void downloadTemplate(HttpServletResponse response) throws Exception {
    ExcelUtils excelUtils = new ExcelUtils();
    List<LookupTableDto> dataList = new ArrayList<>();

    LookupTableDto dto = new LookupTableDto();
    dto.setName("性别示例");
    dto.setCode("sex_example");
    dto.setFullTypeName("分类1/分类2/分类3");
    dto.setRemark("标准描述");

    List<LookupTableValueDto> valueDtoList = new ArrayList<>();

    LookupTableValueDto valueDto1 = new LookupTableValueDto();
    valueDto1.setValue("man");
    valueDto1.setName("男");
    valueDto1.setRemark("男");

    LookupTableValueDto valueDto2 = new LookupTableValueDto();
    valueDto2.setValue("woman");
    valueDto2.setName("女");
    valueDto2.setRemark("女");

    valueDtoList.add(valueDto1);
    valueDtoList.add(valueDto2);
    dto.setValueDtoList(valueDtoList);

    dataList.add(dto);

    // 设置导出参数
    ExportParams params = new ExportParams();
    // 设置excel类型，XSSF代表xlsx，HSSF代表xls
    params.setType(ExcelType.XSSF);
    params.setStyle(ExcelExportTitleStyle.class);
    excelUtils.baseExport(dataList, LookupTableDto.class, "值域标准导入模板", params, response);
  }

  public void export(String typeId, List<String> idList, HttpServletResponse response) {
    ExcelUtils excelUtils = new ExcelUtils();

    try {
      List<String> typeIds = new ArrayList<>();
      if (StringUtils.isNotEmpty(typeId)) {
        commonTypeService.typeIdConvert(typeId, typeIds, CommonTypeEnums.DATA_LOOKUP_TABLE_TYPE);
      }
      List<String> wordSecretList = ThreadContextHelper.getWordSecretList();
      List<LookupTableDto> dataList = this.mapper.getPageList(typeIds, idList, null, null, wordSecretList);
      List<String> typeIdList =
          dataList.stream().map(LookupTableDto::getTypeId).collect(Collectors.toList());
      Map<String, String> fullPathNameMap = commonTypeService.getFullPathNameByIds(typeIdList);
      for (LookupTableDto lookupTableDto : dataList) {
        lookupTableDto.setValueDtoList(
            lookupTableValueService.selectByLookupTableId(lookupTableDto.getId()));
        if (StringUtils.isNotBlank(lookupTableDto.getTypeId())) {
          lookupTableDto.setFullTypeName(fullPathNameMap.get(lookupTableDto.getTypeId()));
        }
      }
      // 设置导出参数
      ExportParams params = new ExportParams();
      // 设置excel类型，XSSF代表xlsx，HSSF代表xls
      params.setType(ExcelType.XSSF);
      params.setStyle(ExcelExportTitleStyle.class);
      excelUtils.baseExport(dataList, LookupTableDto.class, "值域标准", params, response);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public String importLookupTable(MultipartFile file) throws Exception {
    CheckParaUtils.checkObjectValidity(file, logger, "参数[file]不能为空");

    int successNum = 0;
    int errorNum = 0;

    ExcelUtils excelUtils = new ExcelUtils();
    List<LookupTableDto> dataList = excelUtils.importExcel(file, 0, 2, false, LookupTableDto.class);
    if (CollectionUtils.isNotEmpty(dataList)) {
      List<LookupTableDto> errorList = new ArrayList<>();
      Map<String, String> fullTypeNameMap = new HashMap<>();

      for (LookupTableDto data : dataList) {

        String fullTypeName = data.getFullTypeName();
        if (StringUtils.isNotBlank(data.getFullTypeName())) {
          handleType(fullTypeNameMap, data, fullTypeName);

          if (StringUtils.isEmpty(data.getTypeId())) {
            errorNum++;
            data.setErrorMsg("分类路径不存在");
            errorList.add(data);
            logger.info("error");
            continue;
          }

          try {
            if (CollectionUtils.isNotEmpty(data.getValueDtoList())) {
              boolean valid = true;
              for (LookupTableValueDto valueDto : data.getValueDtoList()) {
                if (StringUtils.isAnyEmpty(valueDto.getValue(), valueDto.getName())) {
                  errorNum++;
                  data.setErrorMsg("码值代码或码值名称不能为空");
                  errorList.add(data);
                  valid = false;
                  break;
                }
              }
              if (valid) {
                save(data);
                successNum++;
              }
            } else {
              errorNum++;
              data.setErrorMsg("码值不能为空");
              errorList.add(data);
            }
          } catch (Exception e) {
            errorNum++;
            data.setErrorMsg(e.getMessage());
            errorList.add(data);
          }
        } else {
          errorNum++;
          data.setErrorMsg("分类路径不能为空");
          errorList.add(data);
        }
      }

      String errorImportFilePath = "";
      if (CollectionUtils.isNotEmpty(errorList)) {
        List<LookupTableImportResult> errorList2 =
            errorList.stream()
                .map(
                    item -> {
                      LookupTableImportResult result = new LookupTableImportResult();
                      BeanUtils.copyProperties(item, result);
                      return result;
                    })
                .collect(Collectors.toList());

        // 生成错误信息excel
        // 错误文件存储路径
        String dirPath = ConfigUtils.getInstance().getString("system.data.path") + "errorFile/";
        ExportParams params = new ExportParams();
        params.setType(ExcelType.XSSF);
        params.setStyle(ExcelExportTitleStyle.class);
        errorImportFilePath =
            excelUtils.exportExcel(
                errorList2,
                LookupTableImportResult.class,
                "LookupTableImportError_" + System.currentTimeMillis(),
                params,
                dirPath);
      }

      insertSysExcelImportLog.insertSysExcelImportLog(
          errorImportFilePath,
          dataList.size(),
          successNum,
          errorNum,
          "值域标准导入",
          "bdp_dgs_lookup_table");
    }

    return String.format("总共导入%d条，成功%s条,失败%d条。", dataList.size(), successNum, errorNum);
  }

  private void handleType(
      Map<String, String> fullTypeNameMap, LookupTableDto data, String fullTypeName) {
    String typeId = fullTypeNameMap.get(fullTypeName);
    if (StringUtils.isNotBlank(typeId)) {
      data.setTypeId(typeId);
    } else {
      typeId =
          commonTypeService.getTypeIdByFullName(
              data.getFullTypeName(), typeId, CommonTypeEnums.DATA_LOOKUP_TABLE_TYPE);
      if (StringUtils.isNotBlank(typeId)) {
        fullTypeNameMap.put(fullTypeName, typeId);
        data.setTypeId(typeId);
      }
    }
  }
}
